﻿#include "stdafx.h"
#include "sbcs.h"
#include "extend/sbmem.h"

#ifndef SB_NO_MEM

#if _SB_WINDOWS_
#define _SB_WINHEAP_	1
#endif

#if !defined(_HEAP_MAXREQ)
#ifdef  _WIN64
#define _HEAP_MAXREQ    0xFFFFFFFFFFFFFFE0
#else
#define _HEAP_MAXREQ    0xFFFFFFE0
#endif
#endif

//////////////////////////////////////////////////////////////////////////
// 힙
static struct kImplHeap
{
#if _SB_WINHEAP_
	HANDLE              heap;
#endif

	ksize_t				size;
	ksize_t				max;
	ksize_t				cnt_alloc;
	ksize_t				cnt_free;
} k_impl_heap =
{
#if _SB_WINHEAP_
	NULL,
#endif
	0, 0, 0, 0
};

// 초기화
void _k_heap_init(void)
{
#if _SB_WINDOWS_
#if _SB_WINHEAP_
#if _SB_WINDOWS_DESKTOP_
	k_impl_heap.heap = HeapCreate(0/*|HEAP_NO_SERIALIZE*/, KMEM_PARA_SIZE, 0);
#else
	k_impl_heap.heap = GetProcessHeap();
#endif

	if (!k_impl_heap.heap)
	{
		k_trace(KLV_CRITICAL, "k.heap", "cannot create HEAP");
		k_exit(255);
	}
#endif

#if _SB_DEBUG_
	K_STMT_BEGIN{
		ULONG hfv = 2;
		if (HeapSetInformation(k_impl_heap.heap, HeapCompatibilityInformation, &hfv, sizeof(ULONG)))
			k_trace(KLV_RESULT, "k.heap", "LFH enabled");
	} K_STMT_END;
#endif
#endif
}

// 끝냄
void _k_heap_disp(void)
{
#if _SB_WINHEAP_ && _SB_WINDOWS_DESKTOP_
	if (k_impl_heap.heap)
	{
#if _SB_DEBUG_
		SIZE_T st;

		if (HeapQueryInformation(k_impl_heap.heap, /*HeapEnableTerminationOnCorruption*/(HEAP_INFORMATION_CLASS)1, NULL, 0, &st))
			k_tracef(KLV_RESULT, "hb.heap", "unsafe HEAP count: %d", (int)st);
#endif
		HeapDestroy(k_impl_heap.heap);
		k_impl_heap.heap = NULL;
	}
#endif
}

// 힙 할당
static kpointer _k_heap_alloc(ksize_t size, kuint flags)
{
	kpointer ptr;

#if _SB_WINHEAP_
#if _MSC_VER && _SB_DEBUG_
	__try
	{
		ptr = HeapAlloc(k_impl_heap.heap, (DWORD)flags, size);
	}
	__except (_exception_code() == STATUS_NO_MEMORY ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		if (GetExceptionCode() == STATUS_NO_MEMORY)
			SetLastError(ERROR_NOT_ENOUGH_MEMORY);

		ptr = NULL;
	}
#else
	ptr = HeapAlloc(k_impl_heap.heap, (DWORD)flags, size);
#endif
#else
	/**/
	(void)size;
	(void)flags;
	ptr = NULL;
#endif

	return ptr;
}

// 재할당
static kpointer _k_heap_realloc(kpointer oldptr, ksize_t size, kuint flags)
{
	kpointer ptr;

#if _SB_WINHEAP_
#if _MSC_VER && _SB_DEBUG_
	__try
	{
		ptr = HeapReAlloc(k_impl_heap.heap, (DWORD)flags, oldptr, size);
	}
	__except (_exception_code() == STATUS_NO_MEMORY ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		if (GetExceptionCode() == STATUS_NO_MEMORY)
			SetLastError(ERROR_NOT_ENOUGH_MEMORY);

		ptr = NULL;
	}
#else
	ptr = HeapReAlloc(k_impl_heap.heap, (DWORD)flags, oldptr, size);
#endif
#else
	/**/
	(void)oldptr;
	(void)size;
	(void)flags;
	ptr = NULL;
#endif

	return ptr;
}

// 힘 프리
static void _k_heap_free(kpointer ptr)
{
#if _SB_WINHEAP_
	if (!HeapFree(k_impl_heap.heap, 0, ptr))
		errno = EINVAL;
#else
	/**/ (void)ptr;
#endif
}

// 힙 맞나?
static kcham _k_heap_is_valid(kpointer ptr)
{
#if _SB_WINHEAP_ && _SB_WINDOWS_DESKTOP_
	return HeapValidate(k_impl_heap.heap, 0, ptr) != 0;
#else
	/**/ (void)ptr;
	return TRUE;
#endif
}


//////////////////////////////////////////////////////////////////////////
// 안전하지 않는 메모리

/// 힙 크기.
/// @date 2013-12-22
/// @return A ksize_t.
ksize_t k_heapsize(void)
{
	return k_impl_heap.size;
}

/// 힙 최대 크기.
/// @date 2013-12-22
/// @return A ksize_t.
ksize_t k_heapmax(void)
{
	return k_impl_heap.max;
}

/// 힙 포인터.
/// @date 2013-12-22
/// @return 포인터 값.
kpointer k_getheap(void)
{
#if _SB_WINHEAP_
	return (kpointer)k_impl_heap.heap;
#else
#if _SB_WINDOWS_
	return (kpointer)GetProcessHeap();
#else
	return NULL;
#endif
#endif
}

/// 힙 할당.
/// @date 2013-12-22
/// @param size 크기.
/// @return 포인터 값.
kpointer k_heapalloc(ksize_t size)
{
	kpointer ptr;

#if _SB_WINHEAP_
	if (size <= _HEAP_MAXREQ)
	{
		for (;;)
		{
			ptr = _k_heap_alloc(size, HEAP_ZERO_MEMORY);

			if (ptr)
			{
				k_atominc_ptr(&k_impl_heap.cnt_alloc);
				break;
			}
			else
			{
				errno = ENOMEM;
				break;
			}
		}
	}
	else
	{
		errno = ENOMEM;
		ptr = NULL;
	}
#else
	ptr = calloc(size, sizeof(kbyte));

	if (ptr)
		k_atominc_ptr(&k_impl_heap.cnt_alloc);
#endif

	return ptr;
}

/// 힙 할당.
/// @date 2013-12-22
/// @param size 크기.
/// @return 포인터 값.
kpointer k_heapallocf(ksize_t size)
{
	kpointer ptr;

#if _SB_WINHEAP_
	if (size <= _HEAP_MAXREQ)
	{
		for (;;)
		{
			ptr = _k_heap_alloc(size, 0);

			if (ptr)
			{
				k_atominc_ptr(&k_impl_heap.cnt_alloc);
				break;
			}
			else
			{
				errno = ENOMEM;
				break;
			}
		}
	}
	else
	{
		errno = ENOMEM;
		ptr = NULL;
	}
#else
	ptr = malloc(size);

	if (ptr)
		k_atominc_ptr(&k_impl_heap.cnt_alloc);
#endif

	return ptr;
}

/// 힙 재할당.
/// @date 2013-12-22
/// @param oldptr  oldptr.
/// @param newsize newsize.
/// @return 포인터 값.
kpointer k_heaprealloc(kpointer oldptr, ksize_t newsize)
{
	kpointer ptr;

#if _SB_WINHEAP_
	if (newsize <= _HEAP_MAXREQ)
	{
		for (;;)
		{
			ptr = _k_heap_realloc(oldptr, newsize, 0);

			if (ptr)
			{
				k_atominc_ptr(&k_impl_heap.cnt_alloc);
				k_atominc_ptr(&k_impl_heap.cnt_free);
				break;
			}
			else
			{
				errno = ENOMEM;
				break;
			}
		}
	}
	else
	{
		errno = ENOMEM;
		ptr = NULL;
	}
#else
	ptr = realloc(oldptr, newsize);

	if (ptr)
	{
		k_atominc_ptr(&k_impl_heap.cnt_alloc);
		k_atominc_ptr(&k_impl_heap.cnt_free);
	}
#endif

	return ptr;
}

/// 힙 해제.
/// @date 2013-12-22
/// @param ptr 포인터.
void k_heapfree(kpointer ptr)
{
#if _SB_WINHEAP_
	_k_heap_free(ptr);
#else
	free(ptr);
#endif
	k_atominc_ptr(&k_impl_heap.cnt_free);
}

/// 힙 메모리가 맞나 검사.
/// @date 2013-12-22
/// @param ptr 포인터.
/// @return 성공하면 참을 실패하면 거짓을 반환한다.
kcham k_heapisvalid(kpointer ptr)
{
	if (!ptr)
		return FALSE;

	if (!_k_heap_is_valid(ptr))
		return FALSE;

	return TRUE;
}

/// 힙 개수 증가.
/// @date 2013-12-22
/// @param size 크기.
/// @return A ksize_t.
ksize_t k_heapinc(ksize_t size)
{
	volatile ksize_t ret = k_atomget_ptr(&k_impl_heap.size) + size;

	k_atomset_ptr(&k_impl_heap.size, ret);

	if (k_atomget_ptr(&k_impl_heap.max) < ret)
		k_atomset_ptr(&k_impl_heap.max, ret);

	return ret;
}

/// 힙 개수 감소.
/// @date 2013-12-22
/// @param size 크기.
/// @return A ksize_t.
ksize_t k_heapdec(ksize_t size)
{
	volatile ksize_t ret = k_atomget_ptr(&k_impl_heap.size) - size;

	k_atomset_ptr(&k_impl_heap.size, ret);

	return ret;
}

//
#if __GNUC__
/// Heap 함수 반환.
/// @date 2013-12-22
/// @param pf pf.
/// @return A kvoid.
kvoid k_heap_pom(kfunc pf[3])
{
	pf[0] = (kfunc)_k_heap_alloc;
	pf[1] = (kfunc)_k_heap_realloc;
	pf[2] = (kfunc)_k_heap_free;
}
#endif

#endif  // SB_NO_MEM
